import scala.annotation.tailrec
import java.io.StringReader
import scala.io.Source
object Parser extends App {
  @tailrec
  def pretty(lines: List[String], resp: List[String] = Nil, tab: Int = 0): List[String] = {
    lines match {
      case l :: ll if l.startsWith("}") && l.endsWith("{") => pretty(lines.tail, (" " * (tab - 2)) + l.trim :: resp, tab)
      case l :: ll if l.endsWith("{") => pretty(lines.tail, (" " * tab) + l.trim :: resp, tab + 2)
      case l :: ll if l.startsWith("}") => pretty(lines.tail, (" " * (tab - 2)) + l.trim :: resp, tab - 2)
      case l :: ll => pretty(lines.tail, (" " * tab) + l.trim :: resp, tab)
      case Nil => resp.reverse
    }
  }

  def parse(str: Array[Char], buffer: String = "", tokens: List[String] = Nil): List[String] = {
    if (str.length > 0) {
      str.head match {
        case a @ ('+' | ')' | '(' | '{' | '}' | ';' | ':' | '[' | ']') => parse(str.tail, "", a.toString() :: buffer.trim :: tokens)
        case a @ (' ' | '\n' | '\r') if buffer != "" => parse(str.tail, "", buffer.trim :: tokens)
        case a => parse(str.tail, buffer.trim + a, tokens)
      }
    } else {
      tokens.reverse.filter(_.trim != "")
    }
  }

  def toScala(tokens: List[String], resp: List[String] = Nil): List[String] = {
      def to1(s: String, a: String): List[String] = {
        toScala(tokens.drop(2), s.format(a) :: resp)
      }
      def to(s: String): List[String] = {
        toScala(tokens.drop(1), s :: resp)
      }

    tokens match {
      case "programa" :: p :: _ => to1("object %s extends App", p)
      case a :: _ => a match {
        case "se"                    => to("if (")
        case "entao"|"então"                 => to(")")
        case "senao"|"senão"                 => to("else")
        // Repetição
        case "para"                  => to("for")
        case "gerar"                 => to("yield")
        case "ate"|"até"                   => to("to")
        // Lógico
        case "e"                     => to("&&")
        case "ou"                    => to("||")
        case "não"                   => to("!")
        // Tipos
        case "Texto"                 => to("String")
        case "Inteiro"               => to("Int")
        case "Lógico"                => to("Boolean")
        case "Lista"                 => to("List")
        case "Vetor"                 => to("Array")
        case "seja" | "val"          => to("val")
        case "var" | "variável"      => to("var")
        case "novo" | "nova"         => to("new")

        // Funções
        case "escrever" |"escreva"             => to("println")
        case "ler"        |"leia"           => to("readLine")
        case "lerInteiro"            => to("readInt")
        case "devolver" | "retornar" => to("return")

        // Objetos
        case "classe"                => to("case class")
        case "objeto"                => to("case object")
        case "extende"               => to("extends")

        //Casamento de Padrões
        case "case" | "casar"        => to("match")
        case "caso"                  => to("case")

        case "função"            |"funçao"    => to("def")
        case "{"                     => to("{\n")
        case "}"                     => to("\n}")
        case ";"                     => to("\n")
        case t                       => to(t)
      }
      case Nil => resp.reverse
    }
  }

  val x = """
    programa xyz {
      função f(x: Texto) = "Olá " + x;
      val y = para(i<-1 até 4) gerar i;
      val x = 10 + 30 * x;
      se x==10 então {"sim"} senão {"não"}
    }
    """

  val s = Source.fromFile(args(0)).toList.mkString("")

  val y = parse(s.toCharArray())
  println(pretty(toScala(y).mkString(" ").split("\n").toList).mkString("\n"))
}
